#ifndef TEXT_LAYOUT_H
#define TEXT_LAYOUT_H

#include <wx/string.h>
#include <vector>
#include "note.h"

using std::vector;

/// class used to describe single region of text in TextLayout
class TextRegion {
public:
    enum Type {         ///< type of region
        FIRST=0,
        Unknown,
        Heading,
        Date,
        Text,
        Keyword,           // eg. "Ottovi" in "Ottovi {Ernest Otto}", or "Ernest Otto" in "{{Ernest Otto}}"
        KeywordMark,       // eg. " {Ernest Otto}" in "Ottovi {Ernest Otto}", or "{{" and "}}" in "{{Ernest Otto}}"
        Source,
        Error,
        LAST
    } type;

    int start;      ///< text offset where region starts
    int end;        ///< text offset of the first byte behind end of region (eg. to-from = length of region)
    wxString data;  ///< data for region - currently only to associate keyword with text in marks

    TextRegion(Type type, int start, int end, const wxString& data=wxEmptyString) {
        wxASSERT(type > FIRST && type < LAST);
        wxASSERT(start >= 0);
        wxASSERT(end > start);
        this->start = start;
        this->end = end;
        this->type = type;
        this->data = data;
    }
};

/// Class describes layout of text, divided to regions
class TextLayout {
private:
    int text_len;
    wxString text;
    vector<TextRegion> regions;

    /// Creates region of specified type over area between text offsets.
    /// All existing regions within that area are overwritten.
    void SetRegion(
        TextRegion::Type type,  ///< Type of text region to create.
        int from,               ///< Starting offset of region to create.
        int to,                 ///< Ending offset of region to create (text offset of first char behind the region, eg, to-from = length of region).
        const wxString& data = wxEmptyString   ///< optional string data associated with region
        );

public:

    /// Constructor for text. Text cannot change further.
    /// Describes entire text as "Unknown" type.
    TextLayout(
            const wxString& text=wxEmptyString        ///< text which will be described by this object.
            )
    {
        text_len = text.Len();
        this->text = text;
        if (text_len > 0)
            regions.push_back(TextRegion(TextRegion::Unknown, 0, text_len));
    }

    /// Helper constructor. Takes Note object, and automatically analyzes its body.
    TextLayout(
            const Note& note            ///< Note whose text layout will be analyzed
            )
    {
        text = note.text;
        text_len = text.Len();
        wxASSERT(text_len > 0);     // TODO: what about empty notes?
        regions.push_back(TextRegion(TextRegion::Unknown, 0, text_len));
        AnalyzeMarkText(0, text_len);
    }

    /// Debug-only method, checks whether entire text is covered with consectutive regions.
    void CheckIntegrity();

    /// Looks up region index which contains specified text offset.
    /// @return Index of region, always valid. Use operator [] to access the region.
    int LookupPos(
        int pos           ///< Text offset to look up.
        );

    /// returns length of text
    inline int TextLen() {return text_len;}

    /// returns number of regions
    inline int RegionCount() {return regions.size();}

    /// Access to region description by region index
    const TextRegion& RegionDesc(int index) const
    {
        wxASSERT(index>=0 && index<(int)regions.size());
        return regions[index];
    }

    /// Access to region text by region index
    const wxString RegionText(int index) const
    {
        wxASSERT(index>=0 && index<(int)regions.size());
        wxASSERT(regions[index].start >= 0);
        wxASSERT(regions[index].start < regions[index].end);
        wxASSERT(regions[index].end <= text_len);
        return text.Mid(regions[index].start, regions[index].end - regions[index].start);
    }

    /// Analyze current text as mark text
    void AnalyzeMarkText(int from=0, int to=-1);

    /// Analyze paragraph from current mark text
    void AnalyzeMarkText_Paragraph(int from, int to);

    /// Analyze parenthesis within current mark text
    void AnalyzeMarkText_Parenthesis(int from, int to);
    static bool IsSourceParenthesis(const wxString& text, int from, int to);

    /// Appends text+region at the end of current text
    void AppendRegion(const wxString& new_text, TextRegion::Type region_type, const wxString& region_data=wxEmptyString);

    /// Appends and analyzes mark text
    void AppendMarkText(const wxString& mark_text);

    /// Appends different TextLayout at the end of this one
    void AppendLayout(TextLayout layout2) {
        for (int index=0; index<layout2.RegionCount(); index++)
            AppendRegion(layout2.RegionText(index), layout2.RegionDesc(index).type, layout2.RegionDesc(index).data);
    }
};

#endif
